﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Xml.Serialization;

namespace WhiteFlow.DevTrackingTool.UI.Models
{
    /// <summary>
    /// Contains informations about tasks executed in a specific day.
    /// </summary>
    [Serializable]
    public class TrackingDay : WorksheetItem
    {
        #region Constants

        /// <summary>
        /// The property name for CalendarDay property.
        /// </summary>
        public const string CalendarDayPropertyName = "CalendarDay";

        /// <summary>
        /// The property name for ExecutedTasks property.
        /// </summary>
        public const string ExecutedTasksPropertyName = "ExecutedTasks";

        /// <summary>
        /// The property name for ExpenseReport property.
        /// </summary>
        public const string ExpenseReportPropertyName = "ExpenseReport";

        /// <summary>
        /// The property name for TravelTime property.
        /// </summary>
        public const string TravelTimePropertyName = "TravelTime";

        /// <summary>
        /// The property name for OffSite property.
        /// </summary>
        public const string OffSitePropertyName = "OffSite";

        /// <summary>
        /// The property name for MoveType property.
        /// </summary>
        public const string MoveTypePropertyName = "MoveType";

        /// <summary>
        /// The property name for Summary property.
        /// </summary>
        public const string SummaryPropertyName = "Summary";

        #endregion

        #region Private Fields

        private TrackingWorksheet owner;
        
        private DateTime calendarDay;
        
        private ObservableCollection<TaskExecution> executedTasks;
        
        private ExpenseReport expenseReport;
        
        private TimeSpan travelTime;
        
        private bool offSite;
        
        private MoveType moveType;
        
        private TrackingDaySummary summary;

        #endregion

        #region Constructors

        /// <summary>
        /// Initializes a new instance of the <see cref="TrackingDay"/> class.
        /// </summary>
        public TrackingDay()
        {
            ExecutedTasks.CollectionChanged += new NotifyCollectionChangedEventHandler(OnExecutedTasksCollectionChanged);
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Gets or sets the tracked calendar day.
        /// </summary>
        public DateTime CalendarDay
        {
            get { return calendarDay; }
            set
            {
                calendarDay = value;
                OnPropertyChanged(CalendarDayPropertyName);
                OnWorksheetItemChanged();
            }
        }

        /// <summary>
        /// Gets or sets the list of the tasks executed during the day.
        /// </summary>
        [XmlArray(ElementName = "ExecutedTasks", IsNullable = true)]
        [XmlArrayItem(ElementName = "TaskExecution", IsNullable = false, Type = typeof(TaskExecution))]
        public ObservableCollection<TaskExecution> ExecutedTasks
        {
            get
            {
                if (executedTasks == null)
                    executedTasks = new ObservableCollection<TaskExecution>();

                return executedTasks;
            }
            set
            {
                executedTasks = value;
                OnPropertyChanged(ExecutedTasksPropertyName);
                OnWorksheetItemChanged();

                UpdateTasksSummary();
            }
        }

        [XmlElement(ElementName = "ExpenseReport", IsNullable = true, Type = typeof(ExpenseReport))]
        public ExpenseReport ExpenseReport
        {
            get
            {
                if (expenseReport == null)
                {
                    expenseReport = new ExpenseReport();
                    expenseReport.SetOwner(owner);
                }

                return expenseReport;
            }
            set
            {
                expenseReport = value;
                OnPropertyChanged(ExpenseReportPropertyName);
                OnWorksheetItemChanged();
            }
        }

        public bool OffSite
        {
            get { return offSite; }
            set
            {
                offSite = value;
                OnPropertyChanged(OffSitePropertyName);
                OnWorksheetItemChanged();
            }
        }

        public MoveType MoveType
        {
            get { return moveType; }
            set
            {
                moveType = value;
                OnPropertyChanged(MoveTypePropertyName);
                OnWorksheetItemChanged();
            }
        }

        [XmlIgnore]
        public TimeSpan TravelTime
        {
            get { return travelTime; }
            set
            {
                travelTime = value;
                OnPropertyChanged(TravelTimePropertyName);
                OnWorksheetItemChanged();
            }
        }

        [XmlElement(ElementName = "TravelTime", IsNullable = false)]
        public long TravelTimeTicks
        {
            get { return TravelTime.Ticks; }
            set { TravelTime = new TimeSpan(value); }
        }

        #endregion

        #region Public Non-Serialized Properties

        /// <summary>
        /// Gets or sets the summary for this tracking day.
        /// </summary>
        /// <value>
        /// The summary for this tracking day.
        /// </value>
        [XmlElement(ElementName = SummaryPropertyName, IsNullable = true, Type = typeof(TrackingDaySummary))]
        public TrackingDaySummary Summary
        {
            get
            {
                if (summary == null)
                    summary = new TrackingDaySummary();
                
                return summary;
            }
            set
            {
                summary = value;
                OnPropertyChanged(SummaryPropertyName);
                OnWorksheetItemChanged();
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Sets the owner of the current tracking day.
        /// </summary>
        /// <param name="owner">The owner.</param>
        public void SetOwner(TrackingWorksheet owner)
        {
            this.owner = owner;

            if (owner != null)
            {
                UpdateAllTasksTotalTimes();
                UpdateExpensesReport();
                UpdateTasksSummary();
            }
        }

        public void UpdateAllTasksTotalTimes()
        {
            TimeSpan sum = new TimeSpan(0, 0, 0);

            foreach (TaskExecution execution in ExecutedTasks)
            {
                sum = sum.Add(execution.TotalTime);
            }

            Summary.AllTasksTotalTime = sum;

            if (owner.WorksheetSettings.ContractWorkdayDuration.CompareTo(Summary.AllTasksTotalTime) < 0)
            {
                Summary.Overtime = Summary.AllTasksTotalTime.Subtract(owner.WorksheetSettings.ContractWorkdayDuration);
            }
        }

        private void UpdateExpensesReport()
        {
            foreach (TrackingDay day in owner.Days)
            {
                if (day.ExpenseReport != null)
                {
                    foreach (Expense expense in day.ExpenseReport.Expenses)
                    {
                        expense.UpdateCalculatedAmount();
                    }
                }
            }
        }

        public void UpdateTasksSummary()
        {
            Summary.TasksSummary = new ObservableCollection<TaskExecutionSummary>();
            
            foreach (TaskExecution task in ExecutedTasks)
            {
                if (String.IsNullOrEmpty(task.TaskTitle))
                    continue;

                TaskExecutionSummary taskSummary = Summary.TasksSummary.FirstOrDefault(s => s.Title != null && s.Title.Equals(task.TaskTitle));

                if (taskSummary == null)
                {
                    taskSummary = new TaskExecutionSummary();
                    taskSummary.Title = task.TaskTitle;
                    taskSummary.TotalTime = task.TotalTime;
                    Summary.TasksSummary.Add(taskSummary);
                }
                else
                {
                    taskSummary.TotalTime = taskSummary.TotalTime.Add(task.TotalTime);
                }
            }
        }

        #endregion

        #region Event Handlers

        private void OnExecutedTasksCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            //if (e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Move ||
            //    e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Remove ||
            //    e.Action == System.Collections.Specialized.NotifyCollectionChangedAction.Reset)
            //{
            //    OnPropertyChanged("ExecutedTasks");
            //}

            OnPropertyChanged("ExecutedTasks");
            UpdateTasksSummary();
        }

        #endregion
    }
}